package java_core.Node.Day5_14;

import java.util.*;

/* 环形链表 141
输入一个链表的头结点, 判断该链表是否是环形链表
https://leetcode-cn.com/problems/linked-list-cycle/solution/huan-xing-lian-biao-by-leetcode/
*/
public class HuanXingLianBiao {

    public static void main(String[] args) {
        ListNode node1 = new ListNode(2);
        ListNode node2 = new ListNode(4);
        node1.next = node2;
        ListNode node3 = new ListNode(3);
        node2.next = node3;
        ListNode node4 = new ListNode(5);
        //node3.next = node4;
        ListNode node5 = new ListNode(6);
        node4.next = node5;
        ListNode node6 = new ListNode(4);
        node5.next = node6;
        getNodes(node5);
        getNodes(node6);
        ListNode node = getIntersectionNode2(node1, node5);
        getNodes(node);
        //System.out.println(myisPalindrome1(node4));
//        getNodes(myreverseList2(node1));
    }


//----------------------------------------------------------------------------------------------------------------------

    //141 环形链表  start------------------------

    //141. 1.哈希表法
    public boolean myhasCycle1(ListNode head) {
        Set<ListNode> set = new HashSet<>();
        ListNode node = head;
        //将节点添加入Set中,如果下一个节点能在Set中找到说明是环形链表
        while (node != null) {

            if (set.contains(node)) {
                return true;
            }
            set.add(node);
            node = node.next;

        }
        return false;
    }

    // 141. 2, 双指针法:使用一个快指针一个慢指针一起遍历,如果是环形链表,那么他们迟早会相遇
    public boolean hasCycle2(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        ListNode slow = head;
        ListNode fast = head.next;
        while (slow != fast) {
            if (fast == null || fast.next == null) {
                return false;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        return true;
    }

    // 141 环形链表 end--------------------------------

//----------------------------------------------------------------------------------------------------------------------

    //83.删除排序链表的重复元素 start----------------------

    //me
    private static ListNode deleteRenode(ListNode head) {
        if (head == null) {
            return null;
        } else {
            ListNode curr = head;
            while (curr.next != null) {
                ListNode next = curr.next;
                //如果下一个节点值和当前节点相同,则将当前节点的next指向下下个节点,不相同则将下一个节点设为当前节点
                if (next.val == curr.val) {
                    curr.next = next.next;

                } else {
                    curr = next;
                }

            }
            return head;

        }

    }


    class Solution {
        //83
        public ListNode deleteDuplicates(ListNode head) {
            ListNode cur = head;
            while (cur != null && cur.next != null) {
                if (cur.val == cur.next.val) {
                    ListNode node = cur.next;
                    cur.next = node.next;
                    node.next = null;//清除野指针
                } else {
                    cur = cur.next;
                }

            }
            return head;
        }
    }
    //83.删除排序链表的重复元素 end----------------------

//----------------------------------------------------------------------------------------------------------------------

    //82. 删除排序链表中的重复元素 II 给定一个排序链表，删除所有含有重复数字的节点，只保留原始链表中 没有重复出现 的数字。
    // https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii/
    public static ListNode deleteDuplicates(ListNode head) {
        if (head == null) {
            return null;
        }
        // 1. 创建一个首节点,处理原先的首节点被删除的情况
        ListNode first = new ListNode(0);
        first.next = head;
        ListNode curr = head;
        ListNode pre = first;
        ListNode next;
        boolean f = false;
        while (curr != null) {
            next = curr.next;
            if (next == null) {
                return first.next;
            }
            //查看是否有重复节点
            while (curr.val == next.val) {
                f = true;
                next = next.next;
                if (next == null) {
                    pre.next = null;
                    return first.next;
                }
            }
            //有重复节点
            if (f) {
                //将前一个节点的next指向next
                pre.next = next;
                curr = next;
                f = false;
            } else {
                //没有重复节点
                pre = curr;
                curr = next;
            }

        }

        return first.next;
    }


//----------------------------------------------------------------------------------------------------------------------

    //206 反转链表 start ---------------------------------------

    //206.反转链表 Yes
    private static ListNode myreverseNode1(ListNode head) {
        if (head == null) {
            return null;
        } else {
            if (head.next == null) {
                return head;
            }
            ListNode curr = head;
            //首节点
            ListNode first = head;
            ListNode next;
            //反转的过程:1.将下一个节点的next指向首节点;2.将当前节点的next指向下下个节点
            while (curr.next != null) {
                //1. 先用一个指针记录住下一个节点
                next = curr.next;
                //2.将当前指针的next指向下下个节点
                curr.next = next.next;
                //3.将原先记录下来的下一个节点的next指向首节点
                next.next = first;
                //4.更新首节点
                first = next;
            }

            return first;
        }

    }

    //206 OK
    public static ListNode myreverseList2(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;//当前节点
        while (cur != null) {
            ListNode next = cur.next;//先将下一个节点保存
            cur.next = pre;//当前节点的指针指向上一个节点

            pre = cur;//更新循环
            cur = next;
        }

        return pre;
    }

    // ok
    public static ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode curr = head;
        while (curr != null) {
            ListNode nextTemp = curr.next;//存储下一个节点
            curr.next = prev;
            prev = curr;
            curr = nextTemp;
        }
        return prev;
    }

    //206. 反转链表 end----------------------------------------

//----------------------------------------------------------------------------------------------------------------------

    // 234.回文链表: 1->2->2->1
    // https://leetcode-cn.com/problems/palindrome-linked-list/

    // 1. me 遍历到数组中, 通过数组双指针判断是否回文
    public static boolean myisPalindrome1(ListNode head) {
        if (head == null) {
            //空字符串也属于回文
            return true;
        }
        List<Integer> list = new ArrayList<>();
        while (head != null) {
            list.add(head.val);
            head = head.next;
        }
        int left = 0;
        int right = list.size() - 1;
        while (left < right) {
            if (!list.get(left++).equals(list.get(right--))) {
                return false;
            }
        }
        return true;
    }


//----------------------------------------------------------------------------------------------------------------------

    // 2. 两数相加 no
    public static ListNode myaddTwoNumbers(ListNode l1, ListNode l2) {
        ListNode curr1 = l1;
        ListNode curr2 = l2;
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        List<Integer> list1 = new ArrayList<>();
        List<Integer> list2 = new ArrayList<>();
        while (l1 != null) {
            list1.add(l1.val);
            l1 = l1.next;
        }
        while (l2 != null) {
            list2.add(l2.val);
            l2 = l2.next;
        }
        int length1 = list1.size();
        int length2 = list2.size();

        if (length1 >= length2) {
            ListNode one = curr1;
            for (int i = 0; i < length1; i++) {
                if (i == 0) {
                    curr1.val = 0;
                }
                if (i < length1 - length2) {

                    curr1.val = list1.get(i)+curr1.val;
                } else {
                    curr1.val = list1.get(i) + list2.get(i-(length1 - length2))+curr1.val;
                }
                if (curr1.next != null) {

                    curr1.next.val = curr1.val / 10;
                    curr1.val = curr1.val % 10;
                } else {
                    if (curr1.val >= 10) {
                        ListNode node = new ListNode(curr1.val / 10);
                        curr1.next = node;
                        curr1.val = curr1.val % 10;
                    }
                }
                curr1 = curr1.next;
            }
            return one;
        } else {
            ListNode two = curr2;
            for (int i = 0; i < length2; i++) {
                if (i == 0) {
                    curr2.val = 0;
                }
                if (i < length2 - length1) {

                    curr2.val = list2.get(i)+curr2.val;
                } else {
                    curr2.val = list2.get(i) + list1.get(i-(length2 - length1))+curr2.val;
                }
                if (curr2.next != null) {

                    curr2.next.val = curr2.val / 10;
                    curr2.val = curr2.val % 10;
                } else {
                    if (curr2.val >= 10) {
                        ListNode node = new ListNode(curr2.val / 10);
                        curr2.next = node;
                        curr2.val = curr2.val % 10;
                    }
                }
                curr2 = curr2.next;
            }
            return two;
        }

    }

    // 两数之和 ok
    public static ListNode addTwoNumbers2(ListNode l1, ListNode l2) {
        ListNode dummyHead = new ListNode(0);
        ListNode p = l1, q = l2, curr = dummyHead;
        int carry = 0;
        while (p != null || q != null) {
            int x = (p != null) ? p.val : 0;
            int y = (q != null) ? q.val : 0;
            int sum = carry + x + y;
            carry = sum / 10;
            curr.next = new ListNode(sum % 10);
            curr = curr.next;
            if (p != null) p = p.next;
            if (q != null) q = q.next;
        }
        if (carry > 0) {
            curr.next = new ListNode(carry);
        }
        return dummyHead.next;
    }




//----------------------------------------------------------------------------------------------------------------------

    static List<ListNode> getNodes(ListNode head) {
        List<ListNode> list = new LinkedList<>();
        StringBuffer stringBuffer = new StringBuffer();
        while (head != null) {
            list.add(head);
            stringBuffer.append(head.val).append(",");
            head = head.next;
        }
        System.out.println(stringBuffer.toString());
        return list;
    }

//----------------------------------------------------------------------------------------------------------------------

    //876. 返回链表的中间节点 start----------------------

    //1 把链表遍历到数组中，通过数组获取中间节点
    public ListNode middleNode1(ListNode head) {
        if (head == null) {
            return null;
        } else {
            List<ListNode> list = new ArrayList<>();
            while (head != null) {
                list.add(head);
                head = head.next;
            }
            return list.get(list.size() / 2);
        }

    }

    //2 遍历一遍获取链表长度，下一次遍历到一半
    public ListNode middleNode2(ListNode head) {

        return null;
    }

    //3.双指针：一个slow = next,一个fast = next.next,当fast遍历结束时，slow正好在中间
    public ListNode middleNode3(ListNode head) {
        ListNode slow = head;
        if (head == null) {
            return null;
        } else {
            ListNode fast = head.next;

            while (fast != null) {
                slow = slow.next;
                fast = fast.next;
                if (fast == null) {
                    return slow;
                } else {
                    fast = fast.next;
                }
            }

        }
        return slow;
    }

    //876. 返回链表的中间节点 end----------------------
//----------------------------------------------------------------------------------------------------------------------


    //21 合并两个有序链表 start --------------
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        List<Integer> list = new LinkedList<>();
        while (l1 != null) {
            list.add(l1.val);
            l1 = l1.next;
        }
        while (l2 != null) {
            list.add(l2.val);
            l2 = l2.next;
        }
        ListNode head = null;
        ListNode now = null;
        ListNode next;
        for (Integer integer : list) {
            if (head == null) {
                head = new ListNode(integer);
                now = head;
            } else {
                next = new ListNode(integer);
                now.next = next;

            }

        }
        return null;
    }

    //21 合并两个有序链表 end --------------------

//----------------------------------------------------------------------------------------------------------------------

    //203 删除链表中等于给定值 val 的所有节点 start-----------------------
    public static ListNode removeElements(ListNode head, int val) {
        if (head == null) {
            return null;
        } else {
            ListNode pre = new ListNode(0);
            pre.next = head;
            //创建一个首节点,处理原先的head节点被删除的情况
            ListNode first = pre;
            while (head != null) {
                //定义一个指向next节点的指针
                ListNode next = head.next;
                //当前节点要删除
                if (head.val == val) {
                    //将当前节点的next置为null
                    head.next = null;
                    //当前节点的前置节点的next指向当前节点的下一个节点
                    pre.next = next;

                } else {
                    //如果不需要删除,就是更新当前节点和pre节点
                    pre = head;
                }
                head = next;
            }
            return first.next;
        }

    }

    //203 移除链表元素 end-----------------------

//----------------------------------------------------------------------------------------------------------------------

    //160 相交链表 找到两个单链表相交的起始节点。 start ------------------
    // https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
    public ListNode getIntersectionNode1(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) {
            return null;
        }
        if (headA == headB) {
            return headA;
        }
        ListNode firstA = headA;
        ListNode firstB = headB;
        int count = 0;
        while (count <= 2) {
            headA = headA.next;
            headB = headB.next;
            if (headA != null && headA == headB) {
                return headA;
            }
            //遍历完第一遍
            if (headA == null) {
                count++;
                headA = firstB;
            }
            if (headB == null) {
                headB = firstA;
            }
        }
        //到这里说明两条链表不相交
        return null;
    }

    //2. A,B链表都走相交前面部分的A+B路程
    public static ListNode getIntersectionNode2(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) return null;
        ListNode pA = headA, pB = headB;
        while (pA != pB) {
            pA = pA == null ? headB : pA.next;
            pB = pB == null ? headA : pB.next;
        }
        return pA;
    }


    //160 相交链表 end ------------------

//-------------------------------------------------------------------------------------------------------------------

    //142. 环形链表 II 返回链表开始入环的第一个节点。 如果链表无环，则返回 null
    // https://leetcode-cn.com/problems/linked-list-cycle-ii/

    //1. 通过Set保存,第一次遇到重复的节点即入环的第一个节点 ok
    public ListNode detectCycle(ListNode head) {
        Set<ListNode> set = new HashSet<>();
        while (head != null) {
            if (set.contains(head)) {
                return head;
            }
            set.add(head);
            head = head.next;
        }
        return null;
    }

//----------------------------------------------------------------------------------------------------------------------


    //19. 删除链表的倒数第N个节点 start --------------
    // https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/solution/hua-jie-suan-fa-19-shan-chu-lian-biao-de-dao-shu-d/

    //1. 遍历一遍获取链表长度, 第二遍遍历到length-n ok
    public static ListNode removeNthFromEnd1(ListNode head, int n) {
        if (head == null) {
            return null;
        } else {
            ListNode first = head;
            int length = 0;
            while (head != null) {
                length++;
                head = head.next;
            }
            head = first;
            if (length < n) {
                return head;
            } else if (length == n) {
                return head.next;
            } else {
                for (int i = 1; i <= length - n; i++) {
                    if (i == length - n) {
                        //只遍历到倒数第二个节点,因此head.next不会为null
                        head.next = head.next.next;
                        return first;
                    } else {
                        head = head.next;
                    }
                }
                return first;
            }

        }

    }

    //2. 双指针: 第一个指针先移动到第n个元素,然后第二个指针一起移动,
    // 当第一个指针移动到尾结点时,说明第一个指针就是倒数第n个节点 ok
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode pre = new ListNode(0);
        pre.next = head;
        ListNode start = pre, end = pre;
        while (n != 0) {
            start = start.next;
            n--;
        }
        while (start.next != null) {
            start = start.next;
            end = end.next;
        }
        end.next = end.next.next;
        return pre.next;
    }

    //3. my双指针:删除倒数第n个节点
    public static ListNode myremoveNthFromEnd3(ListNode head, int n) {
        if (head == null) {
            return null;
        }

        ListNode fast = null;
        ListNode slow = null;
        while (n > 0) {
            if (fast == null) {
                fast = head;
            } else {
                fast = fast.next;
            }
            //n大于链表长度
            if (fast == null) {
                return head;
            }
            n--;
        }

        while (fast != null) {
            if (fast.next == null) {
                //说明删除的是第一个节点
                if (slow == null) {
                    return head.next;
                }
                //删除节点
                ListNode next = slow.next;
                slow.next = next.next;
                return head;

            } else {
                //更新slow和fast
                if (slow == null) {
                    slow = head;
                } else {
                    slow = slow.next;
                }
                fast = fast.next;

            }
        }
        //到这说明n<=0
        return head;
    }

    //19. 删除链表的倒数第N个节点 end --------------

//----------------------------------------------------------------------------------------------------------------------

    static class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
            next = null;
        }
    }


}
